{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ca78d85c-d3a3-4b64-a453-bffb55b5d604",
   "metadata": {},
   "outputs": [],
   "source": [
    "##导包--> 读数据 --数据增强 -- 定义模型 --训练 --测试"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6aafe40f-62dd-4d4a-9b43-2211fce24458",
   "metadata": {},
   "outputs": [],
   "source": [
    "#import package\n",
    "import os\n",
    "import numpy as np\n",
    "import cv2\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torchvision.transforms as transforms\n",
    "import pandas as pd\n",
    "import time\n",
    "import torch.utils.data import DataLoader,Dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "78ecc25c-5d1e-4dcb-a99e-c43da6a964dd",
   "metadata": {},
   "outputs": [],
   "source": [
    "## read data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9a56ba07-66d6-49f2-b226-297541300cf2",
   "metadata": {},
   "outputs": [],
   "source": [
    "def readfile(path,label):\n",
    "    img_dir = sorted(os.listdir(path))\n",
    "    #print(os.listdir(path))\n",
    "\n",
    "    x = np.zeros((len(img_dir),128,128,3),dtype = np.unit8)\n",
    "    y = np.zeros((len(img_dir)), dtpye = np.unit8)\n",
    "    for i, file in enumerate(img_dir):\n",
    "        img = cv2.imread(os.path.join(path,file))\n",
    "        x[i,:,:] = cv2.resize(img,(128,128))\n",
    "        if label:\n",
    "            y[i] = int(file.split(\"_\"))\n",
    "    if label:\n",
    "        return x,y\n",
    "    else:\n",
    "        return x\n",
    "\n",
    "workspace_dir = r\n",
    "print('Reading')\n",
    "print('...')\n",
    "train_x,train_y = readfile(os.path.join(workspace_dir,'training'), True)\n",
    "\n",
    "val_x,valn_y = readfile(os.path.join(workspace_dir,'validation'), True)\n",
    "\n",
    "test_x,test_y = readfile(os.path.join(workspace_dir,'testing'), False)\n",
    "\n",
    "print('complete!')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1e118d27-b3e3-45d3-b8bb-8849f5f5430a",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "547479b7-8cdb-474b-a792-73eca726ce6a",
   "metadata": {},
   "outputs": [],
   "source": [
    "#数据增强\n",
    "train_teansform = transforms.Compose([\n",
    "    transforms.ToPLImage(),\n",
    "    transforms.RandomHorizontalFilp(),\n",
    "    transforms.RandomRottation(15),\n",
    "    transforms.ToTensor(),\n",
    "])\n",
    "\n",
    "test_transform = transforms.Compose([\n",
    "    transforms.ToPILImage(),\n",
    "    transforms.ToTensor(),\n",
    "])\n",
    "\n",
    "Class ImgDataset(Dataset):\n",
    "    def __init__(self,x,y = None,transforms = None):\n",
    "        self.x = x\n",
    "        self.y = y\n",
    "        if y is not None:\n",
    "            self.y = torch.LongTensor(y)\n",
    "        self.transform = transform\n",
    "\n",
    "    def __len__(self):\n",
    "        return len(self.x)\n",
    "\n",
    "    def __getitem__(self,index):\n",
    "        X = self.x[index]\n",
    "        if self.transform is not None:\n",
    "            X = self.transform(X)\n",
    "        if self.y is not None:\n",
    "            Y = self.y(index)\n",
    "            return X,Y\n",
    "        else:\n",
    "            return X\n",
    "#定义训练配置\n",
    "batch_size = 32\n",
    "train_set = ImgDataset(train_x,train_y,train_transform)\n",
    "val_set =ImgDataset(val_x,val_y,test_transform)\n",
    "train_loader = DataLoader(train_set,batch_size = batch_size , shuff = True)\n",
    "val_loader = DataLoader(val_set,batch_size = batch_size , shuff = False)\n",
    "print('Dataset complete!')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ab3cf13a-30ce-49af-97a3-7a4b69885cfc",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e09a3df9-9826-48fe-b829-29c3a7e4cade",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Classifier(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(Classifier,self).__init__()\n",
    "        self.cnn = nn.Sequential(\n",
    "            nn.Covn2d(3,64,3,1,1),\n",
    "            nn.BatchNorm2d(64),\n",
    "            nn.ReLU(),\n",
    "            nn.MaxPool2d(2,2,0)\n",
    "        \n",
    "            nn.Covn2d(64,128,3,1,1),\n",
    "            nn.BatchNorm2d(128),\n",
    "            nn.ReLU(),\n",
    "            nn.MaxPool2d(2,2,0)\n",
    "        \n",
    "            nn.Covn2d(128,256,3,1,1),\n",
    "            nn.BatchNorm2d(256),\n",
    "            nn.ReLU(),\n",
    "            nn.MaxPool2d(2,2,0)\n",
    "        \n",
    "            nn.Covn2d(256,512,3,1,1),\n",
    "            nn.BatchNorm2d(512),\n",
    "            nn.ReLU(),\n",
    "            nn.MaxPool2d(2,2,0)\n",
    "        \n",
    "            nn.Covn2d(512,512,3,1,1),\n",
    "            nn.BatchNorm2d(512),\n",
    "            nn.ReLU(),\n",
    "            nn.MaxPool2d(2,2,0)\n",
    "        )\n",
    "        self.fc = nn.Sequential(\n",
    "            nn.Liner(512*4*4,1024),\n",
    "            nn.ReLU(),\n",
    "            nn.Liner(1024,512),\n",
    "            nn.ReLU(),\n",
    "            nn.Liner(512,11)\n",
    "        )\n",
    "\n",
    "    def forwrad(self,x):\n",
    "        out = self.cnn(x)\n",
    "        out = out.view(out.size()[0],-1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "18201687-1812-4387-8771-4cf26b340abb",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "82cceaba-0be0-4777-aff6-154c53722b5d",
   "metadata": {},
   "outputs": [],
   "source": [
    "''' Training '''\n",
    "print(\"Training\")\n",
    "print(\"...\")\n",
    "# 使用training set訓練，並使用validation set尋找好的參數\n",
    "model = Classifier().cuda()\n",
    "loss = nn.CrossEntropyLoss()  # 因為是 classification task，所以 loss 使用 CrossEntropyLoss\n",
    "optimizer = torch.optim.Adam(model.parameters(), lr=0.001)  # optimizer 使用 Adam\n",
    "num_epoch = 30  # 迭代30次\n",
    "\n",
    "for epoch in range(num_epoch):\n",
    "    epoch_start_time = time.time()\n",
    "    train_acc = 0.0\n",
    "    train_loss = 0.0\n",
    "    val_acc = 0.0\n",
    "    val_loss = 0.0\n",
    "\n",
    "    model.train()  # 確保 model 是在 train model (開啟 Dropout 等...)\n",
    "    for i, data in enumerate(train_loader):\n",
    "        optimizer.zero_grad()  # 用 optimizer 將 model 參數的 gradient 歸零\n",
    "        train_pred = model(data[0].cuda())  # 利用 model 得到預測的機率分佈 這邊實際上就是去呼叫 model 的 forward 函數\n",
    "        batch_loss = loss(train_pred, data[1].cuda())  # 計算 loss （注意 prediction 跟 label 必須同時在 CPU 或是 GPU 上）\n",
    "        batch_loss.backward()  # 利用 back propagation 算出每個參數的 gradient\n",
    "        optimizer.step()  # 以 optimizer 用 gradient 更新參數值\n",
    "\n",
    "        train_acc += np.sum(np.argmax(train_pred.cpu().data.numpy(), axis=1) == data[1].numpy())\n",
    "        train_loss += batch_loss.item()\n",
    "\n",
    "    model.eval()\n",
    "    with torch.no_grad():\n",
    "        for i, data in enumerate(val_loader):\n",
    "            val_pred = model(data[0].cuda())\n",
    "            batch_loss = loss(val_pred, data[1].cuda())\n",
    "\n",
    "            val_acc += np.sum(np.argmax(val_pred.cpu().data.numpy(), axis=1) == data[1].numpy())\n",
    "            val_loss += batch_loss.item()\n",
    "\n",
    "        # 將結果 print 出來\n",
    "        print('[%03d/%03d] %2.2f sec(s) Train Acc: %3.6f Loss: %3.6f | Val Acc: %3.6f loss: %3.6f' % \\\n",
    "              (epoch + 1, num_epoch, time.time() - epoch_start_time, \\\n",
    "               train_acc / train_set.__len__(), train_loss / train_set.__len__(), val_acc / val_set.__len__(),\n",
    "               val_loss / val_set.__len__()))\n",
    "\n",
    "train_val_x = np.concatenate((train_x, val_x), axis=0)\n",
    "train_val_y = np.concatenate((train_y, val_y), axis=0)\n",
    "train_val_set = ImgDataset(train_val_x, train_val_y, train_transform)\n",
    "train_val_loader = DataLoader(train_val_set, batch_size=batch_size, shuffle=True)\n",
    "\n",
    "model_best = Classifier().cuda()\n",
    "loss = nn.CrossEntropyLoss()  # 因為是 classification task，所以 loss 使用 CrossEntropyLoss\n",
    "optimizer = torch.optim.Adam(model_best.parameters(), lr=0.001)  # optimizer 使用 Adam\n",
    "num_epoch = 30\n",
    "\n",
    "for epoch in range(num_epoch):\n",
    "    epoch_start_time = time.time()\n",
    "    train_acc = 0.0\n",
    "    train_loss = 0.0\n",
    "\n",
    "    model_best.train()\n",
    "    for i, data in enumerate(train_val_loader):\n",
    "        optimizer.zero_grad()\n",
    "        train_pred = model_best(data[0].cuda())\n",
    "        batch_loss = loss(train_pred, data[1].cuda())\n",
    "        batch_loss.backward()\n",
    "        optimizer.step()\n",
    "\n",
    "        train_acc += np.sum(np.argmax(train_pred.cpu().data.numpy(), axis=1) == data[1].numpy())\n",
    "        train_loss += batch_loss.item()\n",
    "\n",
    "        # 將結果 print 出來\n",
    "    print('[%03d/%03d] %2.2f sec(s) Train Acc: %3.6f Loss: %3.6f' % \\\n",
    "          (epoch + 1, num_epoch, time.time() - epoch_start_time, \\\n",
    "           train_acc / train_val_set.__len__(), train_loss / train_val_set.__len__()))\n",
    "\n",
    "print(\"Training complicated\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4701259a-f2d0-44aa-b726-3194906a05dc",
   "metadata": {},
   "outputs": [],
   "source": [
    "##测试"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e3f31088-d21b-4104-a6a3-7b526339e25d",
   "metadata": {},
   "outputs": [],
   "source": [
    "'''Testing'''\n",
    "print(\"testing\")\n",
    "print(\"...\")\n",
    "test_set = ImgDataset(test_x,transform = test_transform)\n",
    "test_loader = DataLoader(test_set,batch_size = batch_size,shuffle = False)\n",
    "model_best.eval()\n",
    "predicition =[]\n",
    "with torch.no_grad():\n",
    "    for i,data in enumerate(test_loader):\n",
    "        test_pred = model_best(data.cuda())\n",
    "        test_label = np.argmax(test_pred.cpu().data.numpy(),axis = 1)\n",
    "        for y in test_label:\n",
    "            prdfiction.append(y)\n",
    "\n",
    "with open ('predict.csv','w') as f:\n",
    "    f.write('Id,Category\\n')\n",
    "    for y in enumerate(prediction):\n",
    "        f.write('{},{}\\n'.format(i,y))\n",
    "print(\"Testing complicated\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:base] *",
   "language": "python",
   "name": "conda-base-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
